package com.cps116.friendstracker;

import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.InputSource;

import com.easy.facebook.android.apicall.FQL;
import com.easy.facebook.android.apicall.GraphApi;
import com.easy.facebook.android.error.EasyFacebookError;
import com.easy.facebook.android.facebook.FBLoginManager;
import com.easy.facebook.android.facebook.Facebook;

import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningServiceInfo;
import android.app.Application;
import android.content.Intent;
import android.os.AsyncTask;
import android.util.Log;

public class FriendsTrackerApplication extends Application{

	public static final String TAG = FriendsTrackerApplication.class.getSimpleName();
	public static final String PACKAGE_PATH_DIR = "com.cps116.friendstracker.";

	private ProfileUser user;
	private Facebook facebook;
	private FQL facebookFQL;
	private GraphApi facebookGraph;
	private UserLocationUpdateService userLocationUpdateService;
	private FriendsLocationLiveUpdateService friendsLiveUpateService;
	private FBLoginManager loginManager;

	@Override
	public void onCreate() {
		super.onCreate();
		//create a default user update service...
		userLocationUpdateService =  new UserLocationUpdateService();
		user = new ProfileUser();
	}


	@Override
	public void onTerminate() { // 
		super.onTerminate();
		//terminate the userlocation service...
		userLocationUpdateService.stopService(new Intent(this, FriendsTrackerApplication.class));
		Log.i(TAG, "onTerminated");
	}


	/*
	 * thread-safe accessing methods
	 */
	public synchronized Facebook getFacebook()
	{
		//not doing lazy instantiation...you have to be able to login in order to access facebook...
		//NOTE:** this has to be set up WHEN YOU LOG IN...
		return facebook;	
	}


	public synchronized FQL getFacebookFQL()
	{
		//lazy instantiation to save time
		if (facebookFQL == null)
			facebookFQL = new FQL(facebook);

		return facebookFQL;
	}


	public synchronized GraphApi getFacebookGraph()
	{
		//lazy instantiation to save time
		if (facebookGraph == null)
			facebookGraph = new GraphApi(facebook);

		return facebookGraph;
	}



	public synchronized void setLoginManager(FBLoginManager fbManager) {
		
		//set the login manager...
		loginManager = fbManager;
	}
	
	
	public void setFacebook(Facebook facebook) {
		Log.d("testing", String.format("facebook is setting: facebook" ));

		this.facebook = facebook;
	}


	public UserLocationUpdateService getUserLocationUpdateService() {
		//lazy instantiation

		if (userLocationUpdateService == null)
			userLocationUpdateService = new UserLocationUpdateService();

		return userLocationUpdateService;
	}


	/**
	 * Start individual update service....
	 * @param activity
	 * @param milli_interval
	 */
	public void startNewUserLocationUpdateServiceInterval(Activity activity, Long milli_interval)
	{
		if (userLocationUpdateService == null) {
			userLocationUpdateService = new UserLocationUpdateService();			
		}
		else{ 
			//here we just testing the start service..TODO: get rid of the class when not needed...
			startService(new Intent(activity,UserLocationUpdateService.class));

			//if the service is currently running, we just want to stop the service...
			if (serviceIsRunning(FriendsTrackerApplication.PACKAGE_PATH_DIR + UserLocationUpdateService.class.getSimpleName()))
				if (userLocationUpdateService.getServiceUpdateInterval() == milli_interval)
					//we don't need to reschedule an update service if the previous update time is the same
					return;

			stopService(new Intent(activity, UserLocationUpdateService.class));									
		}

		//we want to add extra message to intent
		Intent startServiceIntent = new Intent(activity, UserLocationUpdateService.class);

		startServiceIntent.putExtra("run_flag", true);
		startServiceIntent.putExtra("delay_interval", milli_interval);
		startService(startServiceIntent);
	}

	
	/**
	 * Start friends live update service for pulling data from designed friends 
	 * 
	 */
	public void startNewFriendsLocationUpdateServiceInterva(Activity activity, long milli_interval)
	{
		//first terminate existing service if there is one...
		terminateLiveFriendsUpdateService(activity);

		Intent startServiceIntent = new Intent(activity, FriendsLocationLiveUpdateService.class);
		startServiceIntent.putExtra("run_flag", true);
		startServiceIntent.putExtra("delay_interval", milli_interval);
		startService(startServiceIntent);
	}


	/**
	 * Here's how you terminate the service...if it is not created for the first time,
	 * we just want to make it...
	 */
	public void terminateLiveFriendsUpdateService(Activity caller) {

		if (friendsLiveUpateService == null) {
			friendsLiveUpateService = new FriendsLocationLiveUpdateService();		
		}
		else
		{
			//check if one already exists... if so, we just want to terminate the current service...
			if (serviceIsRunning(FriendsTrackerApplication.PACKAGE_PATH_DIR + FriendsLocationLiveUpdateService.class.getSimpleName()))
				stopService(new Intent(caller, FriendsLocationLiveUpdateService.class));									
		}
	}


	private boolean serviceIsRunning(String service_name)
	{
		ActivityManager manager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
		for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {

			//print out the service names
			Log.d("FriendsTrackerApplication", service_name);

			if (service_name.equals(service.service.getClassName())) {
				return true;
			}
		}
		return false;
	}



	public boolean getUserLocationUpdateServiceRunningFlag()
	{
		if(userLocationUpdateService != null)
			return userLocationUpdateService.getServiceRunFlag();
		else
		{
			return false;
		}
	}

	/**
	 * Ways of accessing the facebook user objec
	 */
	public String getFacebookUserId() {

		return user.getUserId();
	}

	public void setFaceUserId(String id)
	{
		if (id != null)
			user.setUserId(id);
	}




	public ProfileUser getUser()
	{
		return user;
	}
	
	
	public void fetchMyFriends()
	{
		new FriendFetcher(user).execute(getFacebookFQL());
	}
	
	
	public void logoutFacebook()
	{
		try {
			loginManager.logout(facebook);
		} catch (EasyFacebookError e) {
			Log.d("FriendsTrackerApplication", "logout failed");
		}
	}
	
	
	private class FriendFetcher extends AsyncTask<FQL, String, List<String>>
	{
		private ProfileUser user;
		
		FriendFetcher(ProfileUser u)
		{
			user = u;
		}
		
		
		@Override
		protected void onPostExecute(List<String> result) {
			// TODO Auto-generated method stub
			super.onPostExecute(result);
			user.setFriends(result);
			
		}

		@Override
		protected List<String> doInBackground(FQL... fqls) {
			
			FQL fql = fqls[0];
			try {
				String friends_xml = fql.fqlQuery("SELECT name, uid FROM user WHERE uid " +
							   					  "IN (SELECT uid2 FROM friend WHERE uid1 = me()) ORDER BY name");
				
				Pattern p = Pattern.compile("<name>(.+?)</name>(\\s+)<uid>(.+?)</uid>");
				Matcher m = p.matcher(friends_xml);

				ArrayList<String> friends = new ArrayList<String>();

				boolean result = m.find();
				while(result)
				{
					friends.add(m.group(1) + "," + m.group(3));
					result = m.find();
				}
				
				
				//getting the friends id
				try {
					//get the user ID's from facebook... use post request to 
					String result_xml = fql.fqlQuery("SELECT uid FROM user WHERE uid = me()");

					DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
					DocumentBuilder db = dbf.newDocumentBuilder();
					InputSource is = new InputSource();
					is.setCharacterStream(new StringReader(result_xml));
					Document myDom = db.parse(is);
					Element root = myDom.getDocumentElement();

					String uid =root.getElementsByTagName("uid").item(0).getFirstChild().getNodeValue();

					//we want to set the user_id to the application...this way we are sure that facebook id is set
					//up and we won't get a run time exception error.
					setFaceUserId(uid);
				}
				catch(Exception e)
				{
					//this will set the user id to the 
					Log.d("ProfileActivity", "fetchUserId failed...");
				}
				
				
				return friends;
				
				
			} catch (EasyFacebookError e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
			
			
			return null;
		}
		
	}

	
}
